vt-d: enhance the support of Interrupt Remapping EIM and x2APIC
authorKeir Fraser <keir.fraser@citrix.com>
Mon, 7 Sep 2009 07:44:50 +0000 (08:44 +0100)
committerKeir Fraser <keir.fraser@citrix.com>
Mon, 7 Sep 2009 07:44:50 +0000 (08:44 +0100)
1) Clear Interrupt Remapping(IR) unit's CFI (Compatibility Format
Interrupt) to enhance security;
2) Move the iommu_setup() ahead and put it before we begin to use
IOAPIC so we can make sure after we enable Interrupt Remapping, the
later IOAPIC (and MSI) initialization would setup IOAPIC RTEs (and
MSI) with remappable format;
3) Enable x2APIC only when all VT-d engines support IR with EIM
(Extended Interrupt Mode). EIM enables external devices to deliver
interrupts to logical processor with >8-bit APIC ID.

Signed-off-by: Dexuan Cui <dexuan.cui@intel.com>
13 files changed:
xen/arch/ia64/xen/xensetup.c
xen/arch/x86/apic.c
xen/arch/x86/genapic/probe.c
xen/arch/x86/setup.c
xen/drivers/passthrough/iommu.c
xen/drivers/passthrough/vtd/dmar.c
xen/drivers/passthrough/vtd/dmar.h
xen/drivers/passthrough/vtd/intremap.c
xen/drivers/passthrough/vtd/iommu.h
xen/drivers/passthrough/vtd/vtd.h
xen/include/asm-x86/genapic.h
xen/include/asm-x86/smp.h
xen/include/xen/iommu.h

index 579fe3c86129530e6825287d4c72afa69347e594..da8de417c26657e5d0fcdd37395300ffcdf591c6 100644 (file)
@@ -630,6 +630,8 @@ printk("num_online_cpus=%d, max_cpus=%d\n",num_online_cpus(),max_cpus);
 
     initialise_gdb(); /* could be moved earlier */
 
+    iommu_setup();    /* setup iommu if available */
+
     do_initcalls();
     sort_main_extable();
 
index b0d6392cda02d12b402ddd536ad8f0def163d2b8..47d614269e8c2e8aa31e2a26cc449ffe76b1dbee 100644 (file)
@@ -848,6 +848,9 @@ void enable_x2apic(void)
 {
     u32 lo, hi;
 
+    if ( !iommu_supports_eim() )
+        return;
+
     rdmsr(MSR_IA32_APICBASE, lo, hi);
     if ( !(lo & MSR_IA32_APICBASE_EXTD) )
     {
@@ -858,7 +861,13 @@ void enable_x2apic(void)
     else
         printk("x2APIC mode enabled by BIOS.\n");
 
-    x2apic_enabled = 1;
+    if ( !x2apic_enabled )
+    {
+        x2apic_enabled = 1;
+        genapic = &apic_x2apic;
+        printk(KERN_INFO "Switched to APIC driver %s.\n",
+                       genapic->name);
+    }
 }
 
 void __init init_apic_mappings(void)
@@ -889,6 +898,8 @@ __next:
      */
     if (boot_cpu_physical_apicid == -1U)
         boot_cpu_physical_apicid = get_apic_id();
+    x86_cpu_to_apicid[0] = get_apic_id();
+    cpu_2_logical_apicid[0] = get_logical_apic_id();
 
     init_ioapic_mappings();
 }
index 295037e8cfe91437c514bd2289a20f48bcb25ef3..eaf1df24c783cfebd4dc906d67e8300a47f8686d 100644 (file)
@@ -14,7 +14,6 @@
 #include <asm/apicdef.h>
 #include <asm/genapic.h>
 
-extern struct genapic apic_x2apic;
 extern struct genapic apic_summit;
 extern struct genapic apic_bigsmp;
 extern struct genapic apic_default;
@@ -22,7 +21,6 @@ extern struct genapic apic_default;
 struct genapic *genapic;
 
 struct genapic *apic_probe[] __initdata = { 
-       &apic_x2apic, 
        &apic_summit,
        &apic_bigsmp, 
        &apic_default,  /* must be last */
index 26bf8503a4992922abeb0bcb279a110c09645ce1..6518152985f6e61cdd97b4bf773c5654fe3574b2 100644 (file)
@@ -904,11 +904,11 @@ void __init __start_xen(unsigned long mbi_p)
 
     generic_apic_probe();
 
+    acpi_boot_init();
+
     if ( x2apic_is_available() )
         enable_x2apic();
 
-    acpi_boot_init();
-
     init_cpu_to_node();
 
     if ( smp_found_config )
@@ -954,6 +954,8 @@ void __init __start_xen(unsigned long mbi_p)
     if ( opt_nosmp )
         max_cpus = 0;
 
+    iommu_setup();    /* setup iommu if available */
+
     smp_prepare_cpus(max_cpus);
 
     spin_debug_enable();
index c6906871d78ae0254884407ca298de2a97e62ca0..6a32dcd84f97880ecf221d6bf449057e26d42b67 100644 (file)
@@ -260,7 +260,7 @@ int deassign_device(struct domain *d, u8 bus, u8 devfn)
     return 0;
 }
 
-static int iommu_setup(void)
+int iommu_setup(void)
 {
     int rc = -ENODEV;
 
@@ -279,7 +279,6 @@ static int iommu_setup(void)
                iommu_pv_enabled ? "en" : "dis");
     return rc;
 }
-__initcall(iommu_setup);
 
 int iommu_get_device_group(struct domain *d, u8 bus, u8 devfn, 
     XEN_GUEST_HANDLE_64(uint32) buf, int max_sdevs)
index fbbc30204f333693db483612fc280f66f0e53660..4386149ac9b91d2c3eb3246f8dfcff1267634fb0 100644 (file)
@@ -357,6 +357,7 @@ acpi_parse_one_drhd(struct acpi_dmar_entry_header *header)
     struct acpi_table_drhd * drhd = (struct acpi_table_drhd *)header;
     void *dev_scope_start, *dev_scope_end;
     struct acpi_drhd_unit *dmaru;
+    void *addr;
     int ret = 0;
     static int include_all = 0;
 
@@ -371,6 +372,9 @@ acpi_parse_one_drhd(struct acpi_dmar_entry_header *header)
     dprintk(XENLOG_INFO VTDPREFIX, "dmaru->address = %"PRIx64"\n",
             dmaru->address);
 
+    addr = map_to_nocache_virt(0, drhd->address);
+    dmaru->ecap = dmar_readq(addr, DMAR_ECAP_REG);
+
     dev_scope_start = (void *)(drhd + 1);
     dev_scope_end = ((void *)drhd) + header->length;
     ret = acpi_parse_dev_scope(dev_scope_start, dev_scope_end,
index bed177adc6de7611224fa6e442a15a72b235f416..105b2beb312b173f2463347563ed8ff0796de7bb 100644 (file)
@@ -50,6 +50,7 @@ struct acpi_drhd_unit {
     struct dmar_scope scope;            /* must be first member of struct */
     struct list_head list;
     u64    address;                     /* register base address of the unit */
+    u64    ecap;
     u8     include_all:1;
     struct iommu *iommu;
     struct list_head ioapic_list;
@@ -109,6 +110,8 @@ do {                                                \
     }                                                           \
 } while (0)
 
+void *map_to_nocache_virt(int nr_iommus, u64 maddr);
+
 int vtd_hw_check(void);
 void disable_pmr(struct iommu *iommu);
 int is_usb_device(u8 bus, u8 devfn);
index 47350732b865d2660bbeb4612f84e6268a9adcb0..1d1840d26ad3bae2d94428378f4753548eefacc6 100644 (file)
@@ -121,6 +121,22 @@ static void set_ioapic_source_id(int apic_id, struct iremap_entry *ire)
                 apicid_to_bdf(apic_id));
 }
 
+int iommu_supports_eim(void)
+{
+    struct acpi_drhd_unit *drhd;
+
+    if ( !iommu_enabled || !iommu_qinval || !iommu_intremap )
+        return 0;
+
+    for_each_drhd_unit ( drhd )
+        if ( !ecap_queued_inval(drhd->ecap) ||
+             !ecap_intr_remap(drhd->ecap) ||
+             !ecap_eim(drhd->ecap) )
+            return 0;
+
+    return 1;
+}
+
 static int remap_entry_to_ioapic_rte(
     struct iommu *iommu, struct IO_xAPIC_route_entry *old_rte)
 {
@@ -635,10 +651,10 @@ int enable_intremap(struct iommu *iommu)
         ir_ctrl->iremap_index = -1;
     }
 
-#if defined(ENABLED_EXTENDED_INTERRUPT_SUPPORT)
+#ifdef CONFIG_X86
     /* set extended interrupt mode bit */
     ir_ctrl->iremap_maddr |=
-            ecap_ext_intr(iommu->ecap) ? (1 << IRTA_REG_EIME_SHIFT) : 0;
+            x2apic_enabled ? (1 << IRTA_REG_EIME_SHIFT) : 0;
 #endif
     spin_lock_irqsave(&iommu->register_lock, flags);
 
@@ -659,13 +675,6 @@ int enable_intremap(struct iommu *iommu)
     iommu_flush_iec_global(iommu);
 
     spin_lock_irqsave(&iommu->register_lock, flags);
-    /* enable comaptiblity format interrupt pass through */
-    gcmd |= DMA_GCMD_CFI;
-    dmar_writel(iommu->reg, DMAR_GCMD_REG, gcmd);
-
-    IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, dmar_readl,
-                  (sts & DMA_GSTS_CFIS), sts);
-
     /* enable interrupt remapping hardware */
     gcmd |= DMA_GCMD_IRE;
     dmar_writel(iommu->reg, DMAR_GCMD_REG, gcmd);
index bcb05dc5f915a1ec4425914ef4b861c284f15fd8..2d979dbd65beaf94774de8270f61cde4afe31009 100644 (file)
 #define ecap_queued_inval(e)     ((e >> 1) & 0x1)
 #define ecap_dev_iotlb(e)        ((e >> 2) & 0x1)
 #define ecap_intr_remap(e)       ((e >> 3) & 0x1)
-#define ecap_ext_intr(e)         ((e >> 4) & 0x1)
+#define ecap_eim(e)              ((e >> 4) & 0x1)
 #define ecap_cache_hints(e)      ((e >> 5) & 0x1)
 #define ecap_pass_thru(e)        ((e >> 6) & 0x1)
 #define ecap_snp_ctl(e)          ((e >> 7) & 0x1)
index 18e87bef0634ecddf5c0d708dfd37ed62a39f593..b81e5b42d6cfbf1d60d0d70ad0a884150e2f6fed 100644 (file)
@@ -100,7 +100,6 @@ struct msi_msg_remap_entry {
 unsigned int get_cache_line_size(void);
 void cacheline_flush(char *);
 void flush_all_cache(void);
-void *map_to_nocache_virt(int nr_iommus, u64 maddr);
 u64 alloc_pgtable_maddr(struct acpi_drhd_unit *drhd, unsigned long npages);
 void free_pgtable_maddr(u64 maddr);
 void *map_vtd_domain_page(u64 maddr);
index b860d2f5119f7f5847874956d11c1d8a028065e5..2eef80e23e98c6057de20a2a16d7ce37b3370a07 100644 (file)
@@ -49,6 +49,7 @@ struct genapic {
        APICFUNC(acpi_madt_oem_check)
 
 extern struct genapic *genapic;
+extern struct genapic apic_x2apic;
 
 void init_apic_ldr_flat(void);
 void clustered_apic_check_flat(void);
index 4abfb42facb0903dd763fc23e4973c43732c4b23..8cd15e2efb3509bcf91fcebf867cfb1e37cccfba 100644 (file)
@@ -49,6 +49,7 @@ extern void zap_low_mappings(l2_pgentry_t *base);
 
 #define MAX_APICID 256
 extern u32 x86_cpu_to_apicid[];
+extern u32 cpu_2_logical_apicid[];
 
 #define cpu_physical_id(cpu)   x86_cpu_to_apicid[cpu]
 
index 402914d603bb63038ff709fda42f553714797f00..fe27141c1ef639f0d549e3972c7512a464541210 100644 (file)
@@ -57,6 +57,9 @@ struct iommu {
     struct intel_iommu *intel;
 };
 
+int iommu_setup(void);
+int iommu_supports_eim(void);
+
 int iommu_add_device(struct pci_dev *pdev);
 int iommu_remove_device(struct pci_dev *pdev);
 int iommu_domain_init(struct domain *d);